package cate.game.activity.uppool;


import cate.common.table.activity.restrictcall.row.CallItemBaseRow;
import cate.common.table.activity.restrictcall.row.CallWayRow;
import cate.common.table.activity.restrictcall.row.RestrictCallBaseRow;
import cate.common.table.activity.restrictcall.row.RestrictCallRewardRow;
import cate.common.table.d.*;
import cate.common.table.item.hero.StarGroupList;
import cate.common.table.item.hero.row.HeroBaseRow;
import cate.common.util.GameResult;
import cate.common.util.random.RWItem;
import cate.common.util.random.RWList;
import cate.game.activity.base.core.RoleActivitySingle;
import cate.game.activity.uppool.msg.DrawRewardShowResp;
import cate.game.activity.uppool.msg.UpPoolActivityInfoResp;
import cate.game.chat.ChatContentParam;
import cate.game.res.MixRes;
import cate.game.res.MixResItem;
import cate.game.res.RewardShow;
import cate.game.role.Role;
import cate.game.role.bag.hero.Hero;
import cate.game.role.system.RoleSystem;
import cate.game.train.hero.TrainHeroHandler;
import com.fasterxml.jackson.annotation.JsonIgnore;
import easy.java.dev.note.NoteClass;
import easy.java.dev.note.NoteField;
import easy.java.practice.errorcode.EcResult;
import org.apache.commons.lang3.StringUtils;

import java.beans.Transient;
import java.util.*;
import java.util.stream.Collectors;

@NoteClass("up池活动")
public class UpPoolActivity extends RoleActivitySingle {
    @NoteField("选中的保底物品id")
    public int selectedId;
    @NoteField("替换up池保底的id")
    public int exchangeId;
    @NoteField("替换up池保底次数")
    public int exchangeTimes;
    @NoteField("被替换的位置")
    public int pos;
    @NoteField("已抽奖次数,用于触发指定保底")
    public int drawTimes;
    @NoteField("积分")
    public int score;
    @NoteField("历史积分，活动期间只加不减")
    public long historyScore;
    @NoteField("抽卡次数奖励领取记录")
    public List<Integer> takeList;
    @NoteField("脸黑次数")
    public int unluckyTimes;
    @NoteField("免费次数")
    public int freeTimes;
    @NoteField("碎钻召唤次数")
    public int m1CallTimes;
    @NoteField("今日召唤次数")
    public int todayCallTimes;

    @NoteField(value = "自然系许愿不同阵营对应英雄（campId,heroId）")
    public Map<Integer, Integer> selectedIdMap;

    @Override
    public void initialize(Role role) {
        super.initialize(role);
        if (takeList == null) {
            takeList = new ArrayList<>();
        }
        if (selectedIdMap == null) {
            selectedIdMap = new HashMap<>();
        }
    }

    //替换掉一个保底池项
    public GameResult<UpPoolActivity> exchangePoolId(int tid, int pos) {
        GameResult<UpPoolActivity> r = new GameResult<>();
        r.data = this;
        RestrictCallBaseRow baseRow = getBaseRow();
        if (baseRow == null) {
            return r.fail("活动配置不存在");
        }
        RestrictCallBaseRow.PickSellParam exchangeParam = baseRow.getExchangeParam(tid);
        if (exchangeParam == null) {
            return r.fail("不存在的选项");
        }
        if (this.exchangeTimes >= baseRow.exchangeLimit) {
            return r.fail("没有替换机会了");
        }
        if (pos <= 0 || pos > 5) {
            return r.fail("位置不存在");
        }
        this.exchangeId = tid;
        this.selectedId = tid;
        this.pos = pos;
        this.exchangeTimes++;
        return r;
    }

    /**
     * 自然系许愿 阵营英雄选择
     *
     * @param camp
     * @param hero
     * @return
     */
    public GameResult<UpPoolActivity> chooseHeroByCamp(int camp, int hero) {
        GameResult<UpPoolActivity> r = new GameResult<>();
        r.data = this;
        RestrictCallBaseRow baseRow = getBaseRow();
        if (baseRow == null) {
            return r.fail("活动配置不存在");
        }
        List<Integer> ids = baseRow.pickSellParams.stream().map(param -> param.sellTid).collect(Collectors.toList());
        if (hero != 0 && !ids.contains(hero)) {
            return r.fail("选择了不存在的英雄");
        }
        selectedIdMap.put(camp, hero);
        return r;
    }


    //选择限定奖励
    public GameResult<UpPoolActivity> chooseItem(int tid) {
        GameResult<UpPoolActivity> r = new GameResult<>();
        r.data = this;
        RestrictCallBaseRow baseRow = getBaseRow();
        if (baseRow == null) {
            return r.fail("活动配置不存在");
        }
        List<Integer> ids = baseRow.pickSellParams.stream().map(param -> param.sellTid).collect(Collectors.toList());
        List<Integer> exIds = baseRow.exchangeParams.stream().map(param -> param.sellTid).collect(Collectors.toList());
        if (!exIds.contains(tid) && !ids.contains(tid) && funcId() != GDFunc.UP_POOL_NATURAL_WISH) {
            return r.fail("选择了不存在的自选奖励");
        }
        this.selectedId = tid;
        return r;
    }

    public GameResult<UpPoolActivity> singleDraw(int type, int camp) {
        GameResult<UpPoolActivity> r = new GameResult<>();
        r.data = this;
        if (type == GDUpPool.FREE && this.freeTimes <= 0) {
            return r.fail("免费次数不足");
        }
        r = draw(type, 1, camp);
        return r;
    }

    public GameResult<UpPoolActivity> tenDraw(int type, int camp) {
        GameResult<UpPoolActivity> r = new GameResult<>();
        r.data = this;
        if (type == GDUpPool.FREE && this.freeTimes < 10) {
            return r.fail("免费次数不足");
        }
        r = draw(type, 10, camp);
        return r;
    }


    public GameResult<UpPoolActivity> draw(int type, int times, int camp) {
        GameResult<UpPoolActivity> r = new GameResult<>();
        r.data = this;
        RestrictCallBaseRow baseRow = getBaseRow();
        if (baseRow == null) {
            return r.fail("活动配置不存在");
        }
        if (this.selectedId == 0 && funcId() != GDFunc.UP_POOL_NATURAL_WISH) {
            return r.fail("up项未选择");
        }
        if (baseRow.dailyLimit > 0 && todayCallTimes >= baseRow.dailyLimit) {
            return r.fail("今日召唤次数已耗尽");
        }
        CallWayRow costRow = getConsumeRow(baseRow.group, times);
        if (costRow == null) {
            return r.fail("找不到消耗配置，请找管理员");
        }
        if (type == GDUpPool.M1_COST && baseRow.m1Limit == 0) {
            return r.fail("不支持钻石抽卡");
        }
        MixRes cost = null;
        if (type == GDUpPool.FREE) {
            this.freeTimes--;
        } else if (type == GDUpPool.M1_COST) {
            if (StringUtils.isBlank(costRow.m1Cost)) {
                return r.fail("未配置钻石消耗");
            }
            if (baseRow.m1Limit > 0 && m1CallTimes + times > baseRow.m1Limit) {
                return r.fail("钻石召唤次数不足");
            }
            cost = new MixRes(costRow.m1Cost);
        } else {
            if (StringUtils.isBlank(costRow.cost))
                return r.fail("未配置消耗道具");
            cost = new MixRes(costRow.cost);
        }
        if (type != GDUpPool.FREE) {
            GameResult<MixResItem> cr = null;
            cr = cost.consume(role);
            if (!cr.ok()) {
                return r.fail(cr);
            }
        }
        if (type == GDUpPool.M1_COST) {
            m1CallTimes += times;
        }
        RewardShow show = new RewardShow();
        DrawRewardShowResp resp = new DrawRewardShowResp(funcId(), show);
        //如果是元宝召唤，消耗成功，添加兑换的经验
        if (type == GDUpPool.M1_COST && StringUtils.isNotBlank(costRow.exp)) {
            MixRes exp = new MixRes(costRow.exp);
            GameResult<Void> addExp = exp.add(role, true);
            if (!addExp.ok()) {
                return r.fail(addExp);
            }
            resp.reward.addList(exp.copy());
        }
//        开始抽卡
        if (funcId() == GDFunc.UP_POOL_NATURAL_WISH) {
            Integer heroId = selectedIdMap.get(camp);
            if (heroId == null) {
                return r.fail("阵营id错误");
            }
            this.selectedId = heroId;
        }
        MixRes draw = draw(times, resp);
        //如果是英雄召唤，就可能要执行放逐逻辑
        if (getType() == GDUpPool.HERO_POOL) {
            sacrificeHero(draw, resp);
        }
        draw.add(role, true, getGDOperation());
        show.addTplRwd(draw);
        if (baseRow.mode > 0) {
            role.getHistory().action.heroCall(baseRow.mode, times);
        }
        if (funcId() == GDFunc.UP_POOL_STRONG_HERO) {
            role.getHistory().action.upPoolStrongCall(times);
        }
        //增加许愿积分
        if (funcId() == GDFunc.UP_POOL_NATURAL_WISH) {
            //碎片
            MixRes fragments = new MixRes();
            fragments.items.addAll(resp.fragments);
            fragments.add(role, true, getGDOperation());
            //积分
            MixRes reward = new MixRes(GDUpPool.WISH_POOL_REWARD_ONE);
            reward.multiply(times);
            role.log.setOperation(getGDOperation());
            reward.add(role, true, getGDOperation());
            resp.reward.add(role, true, getGDOperation());
            resp.reward.addList(reward);
            role.getHistory().action.heroGodAll(times);
            for (int i = 0; i < times; i ++){
                role.getRes().hoard.addition.addTask(GDHoardTask.WISH);
            }
            role.getSystem().update(RoleSystem.GmOper.NATURAL_WISH, times);
        }
        role.sendNow(resp);
        return r;
    }

    public GameResult<UpPoolActivity> takeCallReward(int tid) {
        GameResult<UpPoolActivity> r = new GameResult<>();
        r.data = this;
        RestrictCallBaseRow baseRow = getBaseRow();
        if (baseRow == null) {
            return r.fail("活动配置不存在");
        }
        List<RestrictCallRewardRow> rewardRows = role.getGame().table.restrictCall.reward.getListByGroupId(baseRow.callRewardGroup);
        if (rewardRows.isEmpty()) {
            return r.fail("活动不存在累计奖励");
        }
        RestrictCallRewardRow row = role.getGame().table.restrictCall.reward.get(tid);
        if (row == null) {
            return r.fail("找不到对应的奖励配置");
        }
        if (row.group != baseRow.callRewardGroup) {
            return r.fail("非本期奖励，不可领取");
        }
        if (this.score < row.getTimes()) {
            return r.fail("未达成领奖条件");
        }
        if (this.takeList.contains(tid)) {
            return r.fail("奖励已领取");
        }
        MixRes reward = new MixRes(row.rewardStr);
        reward.add(role, true, getGDOperation(), tid);
        role.getGame().notice.dialogReward(role, reward);
        takeList.add(tid);
        refresh();
        return r;
    }

    private void refresh() {
        int group = getBaseRow().callRewardGroup;
        List<Integer> ids = role.getGame().table.restrictCall.reward.getListByGroupId(group).stream().map(row -> row.id).collect(Collectors.toList());
        if (takeList.containsAll(ids)) {
            int max = role.getGame().table.restrictCall.reward.getMaxTimesByGroup(group);
            if (max == 0) {
                return;
            }
            score -= max;
            takeList.clear();
        }
    }


    private EcResult<RewardShow> sacrificeHero(MixRes drawRewards, DrawRewardShowResp resp) {
        EcResult<RewardShow> r = new EcResult<>();
        if (!role.getRes().callPO.autoResolve) {
            return r.success();
        }
        List<Hero> sacrifices = new LinkedList<>();
        for (MixResItem item : drawRewards.items) {
            HeroBaseRow tpl = role.getGame().table.hero.base.get(item.tid);
            if (tpl.star < 4) {
                sacrifices.add(new Hero(role, tpl.id));
                drawRewards.minusItem(item);
            }
        }
        drawRewards.items = drawRewards.items.stream().filter(i -> i.num != 0).collect(Collectors.toList());
        if (!sacrifices.isEmpty()) {
            r = TrainHeroHandler.calReturn(role, sacrifices, TrainHeroHandler.sacrificeOpt());
            role.getHistory().action.heroSacrifice(sacrifices.size());
            sacrifices.forEach(e -> role.getRes().heroIntroduction.addHero(e.tid, e.getTpl(role.getGame()).protoId));
        }
        resp.sacrifices = sacrifices;
        if (r.ok() && r.data != null) {
            resp.reward.addList(r.data.tplRwd);
        }
        return r;
    }

    private MixRes draw(int times, DrawRewardShowResp resp) {
        MixRes res = new MixRes();
        RestrictCallBaseRow baseRow = getBaseRow();
        if (baseRow == null) {
            return res;
        }
        int rareNum = 0;
        for (int i = 0; i < times; i++) {
            this.drawTimes++;
            MixResItem item = drawItem(rareNum);
            if (item == null) {
                return res;
            }
            if (Objects.requireNonNull(getItemRow(item.tid)).isRare == 0) {
                this.unluckyTimes++;
            } else {
                rareNum++;
                lamp(item);
            }
            //自然系许愿列表显示
            if (funcId() == GDFunc.UP_POOL_NATURAL_WISH) {
                resp.fragments.add(item);
                //概率获得祝福
                int random = (int) (Math.random() * 2);
                MixRes rewardExtra = new MixRes(GDUpPool.WISH_POOL_REWARD_EXTRA);
                if (random > 0) {
                    resp.reward.addList(rewardExtra);
                }
            } else {
                res.addItem(item);
            }

        }
        this.score += times;
        this.historyScore += times;
        this.todayCallTimes += times;
        return res;
    }

    private MixResItem drawItem(int rareNum) {
        MixResItem res = null;
        RestrictCallBaseRow baseRow = getBaseRow();
        if (baseRow == null) {
            return res;
        }
        RWList<UpItem> pool = null;

        //自然系许愿旧数据清除
        /*RestrictCallBaseRow.PickSellParam param = baseRow.getParam(this.selectedId);
        if (param == null && funcId() == GDFunc.UP_POOL_NATURAL_WISH) {
            selectedId = 0;
        }*/
        //如果保底
        if (this.selectedId != 0 && ifCheat()) {
            res = doCheat();
            this.unluckyTimes = 0;
            this.drawTimes = 0;
            role.getHistory().action.upPoolUpTimes();
        } else if (baseRow.unluckyLimit > 0 && this.unluckyTimes >= baseRow.unluckyLimit) {
            //如果防脸黑
            pool = createPool(baseRow.luckyGroup);
            this.unluckyTimes = 0;
        } else if (baseRow.luckyLimit > 0 && rareNum >= baseRow.luckyLimit) {
            //如果防脸白
            pool = createPool(baseRow.unluckyGroup);
        } else {
            //如果抽卡
            baseRow.config.setup();
            StarGroupList.Item group = baseRow.config.get();
            pool = createPool(group.groupId);
        }
        if (pool == null) {
            return res;
        }
        UpItem item = pool.get();
        res = new MixResItem(getType(), item.id, 1);
        if (getBaseRow().callType == GDUpPool.WISH_POOL) {
            res = MixResItem.parse(Objects.requireNonNull(getItemRow(item.id)).propId);
        }
        if (item.id == this.selectedId) {
            this.drawTimes = 0;
        }
        if (item.id == this.selectedId || baseRow.getParam(item.id) != null) {
            role.getHistory().action.upPoolUpTimes();
        }
        return res;
    }

    private boolean ifCheat() {
        RestrictCallBaseRow baseRow = getBaseRow();
        if (baseRow == null) {
            return false;
        }
        if (this.selectedId == this.exchangeId) {
            return this.drawTimes >= baseRow.getExchangeParam(this.selectedId).guarantee;
        } else {
            return this.drawTimes >= baseRow.getParam(this.selectedId).guarantee;
        }
    }

    private MixResItem doCheat() {
        return createResById();
    }

    private RWList<UpItem> createPool(int group) {
        RWList<UpItem> pool = new RWList<>();
        int camp = 0;
        if (getBaseRow().callType == GDUpPool.WISH_POOL && getItemRow(selectedId) != null) {
            camp = getItemRow(selectedId).camp;
        }
        List<CallItemBaseRow> list = getItemListByPoolAndGroup(getBaseRow().callType, group, camp);
        if (list == null || list.isEmpty()) {
            return null;
        }

        if (getBaseRow().callType != GDUpPool.WISH_POOL) {
            List<UpItem> rows = list.stream()
                    .map(row -> {
                        if (Integer.parseInt(row.propId) == this.selectedId
                                && getBaseRow() != null
                                && getBaseRow().getParam(Integer.parseInt(row.propId)) != null) {
                            if (this.selectedId == this.exchangeId) {
                                return new UpItem(Integer.parseInt(row.propId), row.weight + getBaseRow().getExchangeParam(Integer.parseInt(row.propId)).weight);
                            } else {
                                return new UpItem(Integer.parseInt(row.propId), row.weight + getBaseRow().getParam(Integer.parseInt(row.propId)).weight);
                            }
                        } else {
                            return new UpItem(Integer.parseInt(row.propId), row.weight);
                        }
                    }).collect(Collectors.toList());

            pool.addAndSetup(rows);
        } else {

            List<UpItem> rows = list.stream()
                    .map(row -> {
                        if (Integer.parseInt(row.itemPropId) == this.selectedId
                                && getBaseRow() != null
                                && getBaseRow().getParam(Integer.parseInt(row.itemPropId)) != null) {
                            return new UpItem(Integer.parseInt(row.itemPropId), row.weight + getBaseRow().getParam(Integer.parseInt(row.itemPropId)).weight);
                        } else {
                            return new UpItem(Integer.parseInt(row.itemPropId), row.weight);
                        }
                    }).collect(Collectors.toList());

            pool.addAndSetup(rows);
        }
        return pool;
    }

    private void lamp(MixResItem reward) {
        if (getBaseRow().lampId > 0) {
            List<ChatContentParam> params = new ArrayList<>();
            params.add(ChatContentParam.c(role.getBase().name));
            if (getBaseRow().callType == 1) {
                params.add(ChatContentParam.c(ChatContentParam.TYPE_HERO_ID, reward.tid));
            }
            if (getBaseRow().callType == 4) {
                params.add(ChatContentParam.c(ChatContentParam.TYPE_MIX_RES, new MixRes().addItem(reward)));
            }
            role.getGame().notice.lampOnline(role, getBaseRow().lampId, params);
        }
    }


    public void noticeUpdate() {
        role.sendNow(new UpPoolActivityInfoResp(this));
    }

    @Override
    public void onDaySpan(boolean silence) {
        super.onDaySpan(silence);
        if (getBaseRow() == null) {
            return;
        }
        todayCallTimes = 0;
        this.freeTimes = getBaseRow().freeLimit;
        this.m1CallTimes = 0;
        noticeUpdate();
    }

    @Override
    public void onRoleOpen() {
        if (getBaseRow() == null) {
            return;
        }
        this.freeTimes = getBaseRow().freeLimit;
        if (funcId() == GDFunc.UP_POOL_HERO) {
            this.selectedId = getBaseRow().pickSellParams.get(0).sellTid;
        }
    }

    @Override
    public void onRoleEnd() {
        this.selectedId = 0;
        this.score = 0;
        this.historyScore = 0;
        this.unluckyTimes = 0;
        this.drawTimes = 0;
        this.takeList.clear();
        this.m1CallTimes = 0;
        this.selectedIdMap.clear();
        this.exchangeId = 0;
        this.exchangeTimes = 0;
        this.todayCallTimes = 0;
        this.pos = 0;
    }

    @Override
    public void initSend() {
        noticeUpdate();
    }

    @Override
    public void excelCheck() {

    }

    @JsonIgnore
    private RestrictCallBaseRow getBaseRow() {
        return role.getGame().table.restrictCall.base.get(configTid());
    }

    @JsonIgnore
    private CallWayRow getConsumeRow(int group, int times) {
        return role.getGame().table.restrictCall.callWay.getRowByGroupAndTimes(group, times);
    }

    @JsonIgnore
    private CallItemBaseRow getItemRow(int id) {
        switch (funcId()) {
            case GDFunc.UP_POOL_FAIRY:
                return role.getGame().table.restrictCall.fairyPool.getRowByTid(id);
            case GDFunc.UP_POOL_STRONG_HERO:
            case GDFunc.UP_POOL_HERO:
                return role.getGame().table.restrictCall.heroPool.getRowByTid(id);
            case GDFunc.UP_POOL_NATURAL_WISH:
                return role.getGame().table.restrictCall.wishPool.getRowByTid(id);
            default:
                return null;
        }
    }

    @JsonIgnore
    protected int getGDOperation() {
        switch (funcId()) {
            case GDFunc.UP_POOL_FAIRY:
                return GDOperation.UP_POOL_FAIRY;
            case GDFunc.UP_POOL_HERO:
                return GDOperation.UP_POOL_HERO;
            case GDFunc.UP_POOL_STRONG_HERO:
                return GDOperation.UP_POOL_STRONG_HERO;
            case GDFunc.UP_POOL_NATURAL_WISH:
                return GDOperation.UP_POOL_NATURAL_WISH;
            default:
                return GDOperation.NULL;
        }
    }

    @JsonIgnore
    protected List<CallItemBaseRow> getItemListByPoolAndGroup(int type, int group, int camp) {
        switch (type) {
            case GDUpPool.HERO_POOL:
                return role.getGame().table.restrictCall.heroPool.getListByGroup(group);
            case GDUpPool.FAIRY_POOL:
                return role.getGame().table.restrictCall.fairyPool.getListByGroup(group);
            case GDUpPool.WISH_POOL:
                return role.getGame().table.restrictCall.wishPool.getListByGroup(group, camp);
            default:
                return null;
        }
    }

    //根据选中的限定物id构造奖品
    private MixResItem createResById() {
        if (selectedId == 0) {
            return new MixResItem();
        }
        if (funcId() == GDFunc.UP_POOL_NATURAL_WISH) {
            MixResItem res = MixResItem.parse(getItemRow(selectedId).propId);
            /*//获得指定英雄后清除
            selectedId = 0;*/
            return res;
        }
        return new MixResItem(getType(), selectedId, 1);
    }

    @JsonIgnore
    @Transient
    private byte getType() {
        RestrictCallBaseRow row = getBaseRow();
        if (row == null) {
            return -1;
        }
        switch (row.callType) {
            case GDUpPool.HERO_POOL:
                return GDObj.Type.HERO;
            case GDUpPool.FAIRY_POOL:
                return GDObj.Type.FAIRY;
            case GDUpPool.WISH_POOL:
                return GDObj.Type.HERO;
            default:
                return -1;
        }
    }

    private static final class UpItem implements RWItem {
        public int id;

        public double weight;

        public UpItem(int id, double weight) {
            this.weight = weight;
            this.id = id;
        }

        @Override
        @JsonIgnore
        public double getWeight() {
            return weight;
        }
    }
}
